use std::{
    fs,
    io::Read,
    ops::{Add, Mul, Sub},
    time::Instant,
};

use image::{self, GenericImage};
use rayon::iter::{IntoParallelIterator, ParallelIterator};

/// 向量
struct Vector3 {
    x: f32,
    y: f32,
    z: f32,
}

/// 零向量
const ZERO: Vector3 = Vector3 {
    x: 0.0,
    y: 0.0,
    z: 0.0,
};

impl Vector3 {
    fn new(x: f32, y: f32, z: f32) -> Vector3 {
        Vector3 { x, y, z }
    }
    fn new_from_float(f: f32) -> Vector3 {
        Vector3::new(f, f, f)
    }
    /// 向量长度
    /// ```
    /// let v = Vector3::new(1.0, 2.0, 2.0);
    /// assert_eq!(v.length(), 3.0);
    /// ```
    fn length(&self) -> f32 {
        (self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
    }
    fn normalize(&mut self) -> &mut Vector3 {
        let len: f32 = self.length();
        self.x /= len;
        self.y /= len;
        self.z /= len;
        self
    }
    fn cross(&self, other: &Vector3) -> Vector3 {
        Vector3::new(
            self.y * other.z - self.z * other.y,
            self.z * other.x - self.x * other.z,
            self.x * other.y - self.y * other.x,
        )
    }
    /// 向量的每个分量都相乘
    fn multiply(&self, other: &Vector3) -> Vector3 {
        Vector3::new(self.x * other.x, self.y * other.y, self.z * other.z)
    }
    fn powf(&self, other: f32) -> Vector3 {
        Vector3::new(self.x.powf(other), self.y.powf(other), self.z.powf(other))
    }
    fn clone(&self) -> Vector3 {
        Vector3::new(self.x, self.y, self.z)
    }
    fn to_color(&self) -> image::Rgba<u8> {
        image::Rgba([
            (self.x * 256.0) as u8,
            (self.y * 256.0) as u8,
            (self.z * 256.0) as u8,
            255,
        ])
    }
}

impl Add for &Vector3 {
    fn add(self, other: &Vector3) -> Vector3 {
        Vector3::new(self.x + other.x, self.y + other.y, self.z + other.z)
    }
    type Output = Vector3;
}

impl Mul<&Vector3> for f32 {
    fn mul(self, other: &Vector3) -> Vector3 {
        Vector3::new(self * other.x, self * other.y, self * other.z)
    }
    type Output = Vector3;
}

impl Mul<&Vector3> for &Vector3 {
    /// 点乘
    fn mul(self, other: &Vector3) -> f32 {
        self.x * other.x + self.y * other.y + self.z * other.z
    }
    type Output = f32;
}

impl Sub for &Vector3 {
    fn sub(self, other: &Vector3) -> Vector3 {
        Vector3::new(self.x - other.x, self.y - other.y, self.z - other.z)
    }
    type Output = Vector3;
}

/// 分解向量到基底
fn decompose(v1p: &Vector3, v1v2: &Vector3, v1v3: &Vector3) -> (f32, f32) {
    let p_v2: f32 = ((v1p * v1v2) * (v1v3 * v1v3) - (v1p * v1v3) * (v1v2 * v1v3))
        / ((v1v3 * v1v3) * (v1v2 * v1v2) - (v1v3 * v1v2) * (v1v3 * v1v2));
    let p_v3: f32 = ((v1p * v1v3) * (v1v2 * v1v2) - (v1p * v1v2) * (v1v3 * v1v2))
        / ((v1v3 * v1v3) * (v1v2 * v1v2) - (v1v3 * v1v2) * (v1v3 * v1v2));
    (p_v2, p_v3)
}

/// 面
/// 由三点确定一个平面
/// Ax + By + Cz + D = 0
/// 法线`normal`必须规格化
struct Face {
    a: f32,
    b: f32,
    c: f32,
    d: f32,
    v1: Vector3,
    v2: Vector3,
    v3: Vector3,
    normal: Vector3,
}

impl Face {
    /// 由三点确定一个平面
    fn new(v1: Vector3, v2: Vector3, v3: Vector3, mut normal: Vector3) -> Face {
        let a: f32 = (v2.y - v1.y) * (v3.z - v1.z) - (v2.z - v1.z) * (v3.y - v1.y);
        let b: f32 = (v2.z - v1.z) * (v3.x - v1.x) - (v2.x - v1.x) * (v3.z - v1.z);
        let c: f32 = (v2.x - v1.x) * (v3.y - v1.y) - (v2.y - v1.y) * (v3.x - v1.x);
        let d: f32 = -a * v1.x - b * v1.y - c * v1.z;
        normal.normalize();
        Face {
            a,
            b,
            c,
            d,
            v1,
            v2,
            v3,
            normal,
        }
    }

    /// 判断面是否在两点之间  
    /// 返回是否相交, 交点到`start_point`距离, 交点坐标
    fn can_touch1(&self, start_point: &Vector3, end_point: &Vector3) -> (bool, f32, Vector3) {
        let direction: Vector3 = end_point - start_point;
        // direction未规格化
        // 交点 = start_point + direction * scale
        // 平面方程: Ax + By + Cz + D = 0
        // 交点代入平面方程: A*(start_point.x + direction.x*scale) + B*(start_point.y + direction.y*scale) + C*(start_point.z + direction.z*scale) + D = 0
        // 把A, B, C, D乘进去: A*start_point.x + B*start_point.y + C*start_point.z + D + A*direction.x*scale + B*direction.y*scale + C*direction.z*scale = 0
        // 移项: A*direction.x*scale + B*direction.y*scale + C*direction.z*scale = -A*start_point.x - B*start_point.y - C*start_point.z - D
        // (A*direction.x + B*direction.y + C*direction.z)*scale = -A*start_point.x - B*start_point.y - C*start_point.z - D
        let n: f32 = self.a * direction.x + self.b * direction.y + self.c * direction.z;
        if n == 0.0 {
            return (false, -1.0, ZERO);
        }
        let scale: f32 =
            -(self.a * start_point.x + self.b * start_point.y + self.c * start_point.z + self.d)
                / n;
        // 交点在两点之间
        if scale < 0.0 || scale > 1.0 {
            return (false, -1.0, ZERO);
        }
        // 交点在面内
        let point: Vector3 = start_point + &(scale * &direction);
        let v1v2: Vector3 = &self.v2 - &self.v1;
        let v1v3: Vector3 = &self.v3 - &self.v1;
        let v1p: Vector3 = &point - &self.v1;

        let (p_v2, p_v3) = decompose(&v1p, &v1v2, &v1v3);

        if p_v2 < 0.0 {
            return (false, -1.0, ZERO);
        }
        if p_v3 < 0.0 {
            return (false, -1.0, ZERO);
        }
        if p_v2 + p_v3 > 1.0 {
            return (false, -1.0, ZERO);
        }
        return (true, scale, point);
    }

    /// 判断面是否射线上  
    /// 返回是否相交, 交点到`start_point`距离, 交点坐标
    fn can_touch2(&self, start_point: &Vector3, end_point: &Vector3) -> (bool, f32, Vector3) {
        let direction: Vector3 = end_point - start_point;
        let n: f32 = self.a * direction.x + self.b * direction.y + self.c * direction.z;
        if n == 0.0 {
            return (false, -1.0, ZERO);
        }
        let scale: f32 =
            -(self.a * start_point.x + self.b * start_point.y + self.c * start_point.z + self.d)
                / n;
        // 交点在射线上
        if scale < 0.0 {
            return (false, -1.0, ZERO);
        }
        // 交点在面内
        let point: Vector3 = start_point + &(scale * &direction);
        let v1v2: Vector3 = &self.v2 - &self.v1;
        let v1v3: Vector3 = &self.v3 - &self.v1;
        let v1p: Vector3 = &point - &self.v1;

        let (p_v2, p_v3) = decompose(&v1p, &v1v2, &v1v3);

        if p_v2 < 0.0 {
            return (false, -1.0, ZERO);
        }
        if p_v3 < 0.0 {
            return (false, -1.0, ZERO);
        }
        if p_v2 + p_v3 > 1.0 {
            return (false, -1.0, ZERO);
        }
        return (true, scale, point);
    }

    /// 返回面颜色
    fn color(&self) -> Vector3 {
        Vector3::new(1.0, 1.0, 1.0)
    }
}

const SKY_COLOR: Vector3 = Vector3 {
    x: 0.6,
    y: 0.6,
    z: 0.7,
};
const MAX_REFLECT_NUM: u8 = 5;

/// 光线追踪，方向为`ray`，返回颜色
/// ray必须是已经规格化的
fn ray_cast_n(
    start_point: &Vector3,
    ray: &Vector3,
    faces: &Vec<Face>,
    light_point: &Vector3,
    reflect_num: u8,
) -> Vector3 {
    let mut light_intensity: f32 = 1.0;
    if reflect_num > MAX_REFLECT_NUM {
        return ZERO;
    }
    let mut min_scale: f32 = -1.0;
    let mut min_face: usize = 0;
    let mut min_point: Vector3 = ZERO;
    for i in 0..faces.len() {
        let (v, scale, point) = faces[i].can_touch2(start_point, &ray);
        if v {
            if min_scale < 0.0 || scale < min_scale {
                min_scale = scale;
                min_face = i;
                min_point = point;
            }
        }
    }
    // 直射光源
    // min_point = start_point + min_scale * ray
    // |min_point - light_point| = 0.1
    // 展开
    // (min_point.x - light_point.x)^2 + (min_point.y - light_point.y)^2 + (min_point.z - light_point.z)^2 = 0.01
    // 代入
    // ((start_point.x + min_scale * ray.x) - light_point.x)^2 + ((start_point.y + min_scale * ray.y) - light_point.y)^2 + ((start_point.z + min_scale * ray.z) - light_point.z)^2 = 0.01
    // 移项
    // (start_point.x - light_point.x + ray.x * min_scale)^2 + (start_point.y - light_point.y + ray.y * min_scale)^2 + (start_point.z - light_point.z + ray.z * min_scale)^2 = 0.01
    // 完全平方公式展开
    // (start_point.x - light_point.x)^2 + 2 * (start_point.x - light_point.x) * ray.x * min_scale + ray.x^2 * min_scale^2
    // + (start_point.y - light_point.y)^2 + 2 * (start_point.y - light_point.y) * ray.y * min_scale + ray.y^2 * min_scale^2
    // + (start_point.z - light_point.z)^2 + 2 * (start_point.z - light_point.z) * ray.z * min_scale + ray.z^2 * min_scale^2 = 0.01
    // 合并同类项
    // (start_point.x - light_point.x)^2 + (start_point.y - light_point.y)^2 + (start_point.z - light_point.z)^2
    // + 2 * min_scale * ((start_point.x - light_point.x) * ray.x + (start_point.y - light_point.y) * ray.y + (start_point.z - light_point.z) * ray.z)
    // + (ray.x^2 + ray.y^2 + ray.z^2) * min_scale^2 = 0.01
    let a: f32 = ray.x * ray.x + ray.y * ray.y + ray.z * ray.z;
    let b: f32 = 2.0
        * ((start_point.x - light_point.x) * ray.x
            + (start_point.y - light_point.y) * ray.y
            + (start_point.z - light_point.z) * ray.z);
    let c: f32 = (start_point.x - light_point.x) * (start_point.x - light_point.x)
        + (start_point.y - light_point.y) * (start_point.y - light_point.y)
        + (start_point.z - light_point.z) * (start_point.z - light_point.z)
        - 0.01;
    // 计算delta
    // delta = b^2 - 4ac
    let delta: f32 = b * b - 4.0 * a * c;
    if delta > 0.0 {
        return Vector3::new_from_float(light_intensity);
    }

    // 光线打到了天空
    if min_scale < 0.0 {
        let mut intensity: f32 =
            ray.clone().normalize() as &Vector3 * light_point.clone().normalize() as &Vector3;
        intensity = f32::max(0.0, intensity);
        return intensity * &SKY_COLOR;
    }
    // 计算法线
    let normal: &Vector3 = &faces[min_face].normal;
    // 计算反射
    // reflect_ray = ray - 2 * normal * ray * normal
    // 注意normal和ray应该都是单位向量
    let mut reflect_ray: Vector3 = ray - &(2.0 * (normal as &Vector3 * ray) * (normal as &Vector3));
    let reflect_ray: &mut Vector3 = reflect_ray.normalize();
    let reflect_color: Vector3 = ray_cast_n(
        &min_point,
        &reflect_ray,
        faces,
        light_point,
        reflect_num + 1,
    );
    // 检测是否有遮挡
    for i in 0..faces.len() {
        if i == min_face {
            continue;
        }
        let (v, _, _) = faces[i].can_touch1(&min_point, &light_point);
        if v {
            light_intensity = 0.0;
        }
    }
    // 计算灯光方向
    let mut min_light: Vector3 = light_point - &min_point;
    let min_light: &mut Vector3 = min_light.normalize();
    let base_color: Vector3 = faces[min_face].color();
    // 高光指数
    let specular_exponent: f32 = 2.0;
    let light_color: Vector3 = Vector3::new(1.0, 1.0, 1.0);
    // 计算最终颜色
    // C1 = I_l * max(0, N * L) + I_s * max(0, R * L)^p
    // C2 = max(0, N * R) * C_R + C_R^p
    // C = (C1 + 0.5 * C2) * base_color
    let color1: Vector3 =
        &(light_intensity * f32::max(0.0, (min_light as &Vector3) * &normal) * &light_color)
            + &(f32::max(0.0, (reflect_ray as &Vector3) * min_light as &Vector3)
                .powf(specular_exponent)
                * &light_color);
    let color2: Vector3 = &(f32::max(0.0, (normal as &Vector3) * reflect_ray as &Vector3)
        * &reflect_color)
        + &reflect_color.powf(specular_exponent);
    let finnal_color: Vector3 = Vector3::multiply(&(&color1 + &(0.5 * &color2)), &base_color);
    return finnal_color;
}

fn read_faces(path: &str) -> Vec<Face> {
    let mut ret: Vec<Face> = vec![];
    let mut file: fs::File = fs::File::open(path).expect("打开文件出错");
    let mut string: String = String::new();
    file.read_to_string(&mut string).expect("读取文件出错");
    let lines: Vec<&str> = string.split("\n").collect();
    let mut vectors: Vec<Vector3> = vec![];
    let mut normals: Vec<Vector3> = vec![];
    for line in lines {
        let tookens: Vec<&str> = line.split(" ").collect();
        if tookens[0] == "v" {
            vectors.push(Vector3::new(
                tookens[1].parse().unwrap(),
                tookens[2].parse().unwrap(),
                tookens[3].parse().unwrap(),
            ));
        } else if tookens[0] == "vn" {
            normals.push(Vector3::new(
                tookens[1].parse().unwrap(),
                tookens[2].parse().unwrap(),
                tookens[3].parse().unwrap(),
            ));
        } else if tookens[0] == "f" {
            let mut points: Vec<&Vector3> = vec![];
            let mut normal: &Vector3 = &normals[0];
            for i in tookens[1..].iter() {
                let ids: Vec<&str> = i.split("/").collect();
                let point_id: usize = ids[0].parse().unwrap();
                let normal_id: usize = ids[2].parse().unwrap();
                points.push(&vectors[point_id - 1]);
                normal = &normals[normal_id - 1];
            }
            for i in 0..points.len() - 2 {
                let v1: &Vector3 = points[0];
                let v2: &Vector3 = points[i + 1];
                let v3: &Vector3 = points[i + 2];
                ret.push(Face::new(
                    v1.clone(),
                    v2.clone(),
                    v3.clone(),
                    normal.clone(),
                ));
            }
        }
    }
    ret
}

fn move_faces(faces: &Vec<Face>, vector: &Vector3) -> Vec<Face> {
    let mut ret_faces: Vec<Face> = vec![];
    for i in 0..faces.len() {
        ret_faces.push(Face::new(
            &faces[i].v1 + vector,
            &faces[i].v2 + vector,
            &faces[i].v3 + vector,
            faces[i].normal.clone(),
        ));
    }
    ret_faces
}

struct Screen {
    width: i32,
    height: i32,
    scale: f32,
    faces: Vec<Face>,
    light: Vector3,
}

impl Screen {
    fn new(width: i32, height: i32, faces: Vec<Face>) -> Screen {
        Screen {
            width,
            height,
            scale: 2.0 / (width as f32),
            faces,
            light: Vector3::new(0.0, 0.0, 1.0),
        }
    }

    fn ray_cast_pixel(&self, i: i32) -> image::Rgba<u8> {
        let x: i32 = i % self.width;
        let y: i32 = i / self.width;
        let mut camera_ray: Vector3 = Vector3::new(
            (x as f32) * self.scale - 1.0,
            0.3,
            (0.5 * (self.height as f32) - (y as f32)) * self.scale,
        );
        // 一定要规格化
        let camera_ray: &Vector3 = camera_ray.normalize();
        let color: Vector3 = ray_cast_n(&ZERO, &camera_ray, &self.faces, &self.light, 1);
        color.to_color()
    }
}

fn main() {
    let start: Instant = Instant::now();
    let faces: Vec<Face> = read_faces("test.obj");
    let faces: Vec<Face> = move_faces(&faces, &Vector3::new(0.0, 1.5, 0.0));
    let mut screen: Screen = Screen::new(500, 350, faces);
    let mut render_image: image::DynamicImage =
        image::DynamicImage::new_rgb8(screen.width as u32, screen.height as u32);
    println!("分辨率: {}x{}", screen.width, screen.height);
    println!("面数（三角面）: {}", screen.faces.len());

    println!("{:?}", start.elapsed());
    for i in 0..1 {
        screen.light = Vector3::new(-3.0, 3.0, 3.0);
        let input: std::ops::Range<i32> = 0..(screen.width * screen.height);
        let result: Vec<image::Rgba<u8>> = input
            .into_par_iter()
            .map(|i| screen.ray_cast_pixel(i))
            .collect::<Vec<_>>();
        for i in 0..(screen.width * screen.height) {
            render_image.put_pixel(
                i as u32 % screen.width as u32,
                i as u32 / screen.width as u32,
                result[i as usize],
            );
        }
        // 保存图片，格式为png
        render_image
            .save("render".to_owned() + &i.to_string() + ".png")
            .expect("保存图片出错");
        println!("{} {:?}", i, start.elapsed());
    }
    println!("{:?}", start.elapsed());
}
